/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 *
 */
import path from 'path';

import { hvigorCore } from '@ohos/hvigor';
import { getAlignTarget } from '@ohos/hvigor/src/base/util/task-util.js';

import { BuildDirConst } from '../const/build-directory-const.js';
import { CommonConst, DefaultTargetConst } from '../const/common-const.js';
import { CoreModuleModelImpl } from '../model/module/core-module-model-impl.js';
import { RuntimeOnlyObj } from '../options/build/build-opt.js';
import { RouterMapOptions } from '../options/configure/router-map-options.js';
import { OhosPluginId } from '../plugin/common/ohos-plugin-id.js';
import { HarPlugin } from '../plugin/har-plugin.js';
import { Dependency } from '../project/dependency/core/dependency-interface.js';
import { DependencyManager } from '../project/dependency/dependency-manager.js';
import { TargetTaskService } from '../tasks/service/target-task-service.js';
import { OhPackageJsonOpt } from '../tasks/task/ohos-har-task.js';
import { OhosLogger } from './log/ohos-logger.js';
import { TargetUtils } from './target-utils.js';

import RouterMapObj = RouterMapOptions.RouterMapObj;

const DEP_TARGET_CACHE_NAME = 'depTargetsCache';
export class HarTargetUtil {
  private static _log: OhosLogger = OhosLogger.getLogger('HarTargetUtil');

  /**
   * 获取指定har target
   * 规则：命令行指定 > 引用方 target > default
   * 指定ohosTest模块除外
   *
   * @param targetService
   * @param harModuleName
   */
  static getHarTargetName(targetService: TargetTaskService, harModuleName: string): string {
    const harTargetNameList = targetService.getModuleService().getProjectModel().getModuleSpecificTargets().get(harModuleName);
    if (harTargetNameList?.length && harTargetNameList[0] !== DefaultTargetConst.ALL_TARGET) {
      return harTargetNameList[0];
    }
    const entryModuleName = targetService.getTargetData().getTargetName();
    if (entryModuleName === CommonConst.OHOS_TEST) {
      return DefaultTargetConst.DEFAULT_TARGET;
    }
    const harModule = targetService.getModuleService().getProjectModel().getModuleModelByName(harModuleName);
    if (!harModule) {
      return DefaultTargetConst.DEFAULT_TARGET;
    }
    const harModuleTargets = harModule.getTargetOptions().map((target) => target.name);
    const alignTarget = getAlignTarget();
    if (alignTarget && harModuleTargets.includes(alignTarget as string)) {
      return getAlignTarget() as string;
    }
    if (harModuleTargets.includes(entryModuleName)) {
      return entryModuleName;
    }
    return DefaultTargetConst.DEFAULT_TARGET;
  }

  /**
   * 收集的所有的本地依赖har的targetName
   *
   * @param targetService
   */
  static calDepHarTargets(targetService: TargetTaskService): Map<string, string> {
    const depHarTargets = new Map<string, string>();
    const service = targetService.getModuleService();
    const moduleModel = service.getModuleModel();
    const moduleName: string = moduleModel.getName();
    const curTarget = targetService.getTargetData().getTargetName();
    // 若是已解析过的模块就读取缓存，省去foreach时间，有
    const depTargetsCache = hvigorCore.getCache(DEP_TARGET_CACHE_NAME);
    const curDepHarTargets = depTargetsCache.get(`${moduleName}@${curTarget}`);
    if (curDepHarTargets) {
      return curDepHarTargets;
    }

    service.getHarModuleDependencies().forEach((harDependency) => {
      const targetName = this.getHarTargetData(harDependency, moduleModel, curTarget, moduleName, targetService);
      if (targetName) {
        depHarTargets.set(harDependency[0], targetName);
      } else {
        const lastDependencyName = harDependency[1].getLastDependencyName();
        if (lastDependencyName) {
          const lastDependencyTargetName = depHarTargets.get(lastDependencyName);
          if (lastDependencyTargetName) {
            const depTargetName = this.getHarTargetData(harDependency, moduleModel, lastDependencyTargetName, lastDependencyName, targetService);
            if (depTargetName) {
              depHarTargets.set(harDependency[0], depTargetName);
            }
          }
        }
      }
    });
    depTargetsCache.set(`${moduleName}@${curTarget}`, depHarTargets);
    return depHarTargets;
  }

  static getHarTargetData(
    harDependency: [string, Dependency],
    moduleModel: CoreModuleModelImpl,
    curTarget: string,
    moduleName: string,
    targetService: TargetTaskService,
  ) {
    const harName = harDependency[0];
    if (harDependency[1].getLastDependencyName() && harDependency[1].getLastDependencyName() !== moduleName) {
      return '';
    }

    const harModule = moduleModel.getModule().findModuleByName(harName);
    if (!harModule) {
      this._log.printErrorExit('HAR_TARGET_NOT_MATCH', [curTarget, harName], [[harName, moduleName]]);
      return '';
    }
    if (targetService.getModuleService().isFaMode()) {
      return this.getHarTargetName(targetService, harName);
    }
    const harPlugin = harModule.getPluginById(OhosPluginId.OHOS_HAR_PLUGIN) as HarPlugin;

    // 获取模块target数据, 如果命令行有指定则获取指定target，否则获取全部target
    const harTargets = harPlugin.getNeedExecTargetServiceList();
    if (!harTargets || harTargets.length === 0) {
      this._log.printErrorExit('HAR_TARGET_NOT_MATCH', [curTarget, harName], [[harName, moduleName]]);
    }

    // 对target优先级进行整理
    const tryTargets = TargetUtils.pushTryTargets({
      projectModel: targetService.getModuleService().getProjectModel(),
      moduleName: harName,
      superTargetName: curTarget,
    });
    // 根据优先级查找target
    for (const tryTarget of tryTargets) {
      const targetService = harTargets.find((service) => service.getTargetData().getTargetName() === tryTarget);
      if (targetService) {
        return tryTarget;
      }
    }
    return DefaultTargetConst.DEFAULT_TARGET;
  }

  /**
   * 获取本地或远程源码har依赖的worker/runtimeOnly/routerMap-pageSourceFile配置对应集合或对象的Map
   * @param targetTaskService
   */
  static getDep2ConfigMap(targetTaskService: TargetTaskService | undefined): Dep2ConfigObj | undefined {
    if (!targetTaskService) {
      return;
    }

    const moduleTaskService = targetTaskService.getModuleService();
    if (!moduleTaskService) {
      return;
    }

    //  收集本地或远程依赖har与其worker集合对应的map
    const dep2WorkersMap: Map<Dependency, string[]> = new Map<Dependency, string[]>();

    // 收集本地或远程依赖har与其runtimeOnly sources/packages集合对应的map
    const dep2RuntimeOnlySourcesMap: Map<Dependency, string[]> = new Map<Dependency, string[]>();
    const dep2RuntimeOnlyPackagesMap: Map<Dependency, string[]> = new Map<Dependency, string[]>();

    // 收集本地或远程依赖har与其routerMap对象的pageSourceFile集合对应的map
    const dep2RouterMapObjPageSourceFiles: Map<Dependency, string[]> = new Map<Dependency, string[]>();

    // 收集本地或远程依赖har其资源目录集合对应的map
    const dep2ResourcesMap = new Map<Dependency, string[]>();

    // local
    moduleTaskService.getHarModuleDependencies().forEach((dependency) => {
      const depTargetService = this.getDepTargetService(targetTaskService, dependency);
      if (!depTargetService) {
        return;
      }

      // collect local worker
      this.collectWorkers(dep2WorkersMap, depTargetService.getBuildOption().sourceOption?.workers, dependency[1]);

      // collect local runtimeOnly
      this.collectRuntimeOnly(dep2RuntimeOnlySourcesMap, dep2RuntimeOnlyPackagesMap, depTargetService.getBuildOption().arkOptions?.runtimeOnly, dependency[1]);

      // collect local routerMap
      this.collectRouterMapPageSourceFiles(
        dep2RouterMapObjPageSourceFiles,
        depTargetService.getTargetRouterMapObjList(depTargetService.getModuleService().getModuleModel()),
        dependency[1],
      );

      // collect local resources
      this.collectResources(dep2ResourcesMap, depTargetService.getTargetData().getTargetOpt().resource?.directories, dependency[1]);
    });

    // remote
    moduleTaskService.getRemoteHarDependencies().forEach((dependency) => {
      if (dependency.isByteCodeHarDependency()) {
        return;
      }

      const ohPkgJsonObj: OhPackageJsonOpt = dependency.getPackageJsonObj() as OhPackageJsonOpt;

      // collect remote worker
      this.collectWorkers(dep2WorkersMap, ohPkgJsonObj.metadata?.workers, dependency);

      // collect remote runtimeOnly
      this.collectRuntimeOnly(dep2RuntimeOnlySourcesMap, dep2RuntimeOnlyPackagesMap, ohPkgJsonObj.metadata?.runtimeOnly, dependency);

      // collect remote routerMap
      this.collectRouterMapPageSourceFiles(dep2RouterMapObjPageSourceFiles, DependencyManager.getRemoteDependencyRouterMapObjList(dependency), dependency);

      // collect remote resource
      this.collectResources(dep2ResourcesMap, ohPkgJsonObj.metadata?.resource?.directories, dependency);
    });

    return {
      dep2Workers: dep2WorkersMap,
      dep2RuntimeOnlyObj: {
        dep2RuntimeOnlySources: dep2RuntimeOnlySourcesMap,
        dep2RuntimeOnlyPackages: dep2RuntimeOnlyPackagesMap,
      },
      dep2RouterMapObjPageSourceFiles,
      dep2Resources: dep2ResourcesMap,
    };
  }

  /**
   * 收集本地远程依赖源码har的worker
   * @param dep2WorkersMap
   * @param sourceWorkers
   * @param dependency
   * @private
   */
  private static collectWorkers(dep2WorkersMap: Map<Dependency, string[]>, sourceWorkers: string[] | undefined, dependency: Dependency): void {
    if (!sourceWorkers?.length) {
      return;
    }

    dep2WorkersMap.set(dependency, sourceWorkers);
  }

  /**
   * 收集本地远程依赖源码har的runtimeOnly
   * @param dep2RuntimeOnlySourcesMap
   * @param dep2RuntimeOnlyPackagesMap
   * @param runtimeOnlyObj
   * @param dependency
   * @private
   */
  private static collectRuntimeOnly(
    dep2RuntimeOnlySourcesMap: Map<Dependency, string[]>,
    dep2RuntimeOnlyPackagesMap: Map<Dependency, string[]>,
    runtimeOnlyObj: RuntimeOnlyObj | undefined,
    dependency: Dependency,
  ): void {
    if (!runtimeOnlyObj) {
      return;
    }
    if (runtimeOnlyObj.sources) {
      dep2RuntimeOnlySourcesMap.set(dependency, runtimeOnlyObj.sources);
    }
    if (runtimeOnlyObj.packages) {
      dep2RuntimeOnlyPackagesMap.set(dependency, runtimeOnlyObj.packages);
    }
  }

  /**
   * 收集本地远程依赖源码har的routerMap对象的pageSourceFile
   * @param dep2RouterMapObjPageSourceFilesMap
   * @param routerMapObjList
   * @param dependency
   * @private
   */
  private static collectRouterMapPageSourceFiles(
    dep2RouterMapObjPageSourceFilesMap: Map<Dependency, string[]>,
    routerMapObjList: RouterMapObj[] | undefined,
    dependency: Dependency,
  ): void {
    if (!routerMapObjList?.length) {
      return;
    }

    const dep2RouterMapObjPageSourceFiles: string[] = [];
    routerMapObjList.forEach((routerMapObj) => {
      dep2RouterMapObjPageSourceFiles.push(routerMapObj.pageSourceFile);
    });
    dep2RouterMapObjPageSourceFilesMap.set(dependency, [...dep2RouterMapObjPageSourceFiles]);
  }

  /**
   * 收集本地或远程依赖har其资源目录集合对应的map
   * @param dep2ResourcesMap
   * @param resourcesDir
   * @param dependency
   * @private
   */
  private static collectResources(dep2ResourcesMap: Map<Dependency, string[]>, resourcesDir: string[] | undefined, dependency: Dependency): void {
    const DEFAULT_RES_DIR = `${BuildDirConst.SRC}${path.sep}${BuildDirConst.MAIN}${path.sep}${BuildDirConst.RESOURCES}`;
    dep2ResourcesMap.set(dependency, !resourcesDir?.length ? [DEFAULT_RES_DIR] : [...resourcesDir]);
  }

  /**
   * 获取模块依赖模块对应的targetService
   * @param targetService
   * @param dependency
   * @private
   */
  static getDepTargetService(targetService: TargetTaskService | undefined, dependency: [string, Dependency]): TargetTaskService | undefined {
    if (!targetService) {
      return;
    }
    const moduleTaskService = targetService.getModuleService();
    const projectModel = moduleTaskService.getProjectModel();
    const moduleModel = moduleTaskService.getModuleModel();
    const harTargetData = HarTargetUtil.getHarTargetData(
      dependency,
      moduleModel,
      targetService.getTargetData().getTargetName(),
      moduleModel.getName(),
      targetService,
    );
    const moduleModelByDependency = DependencyManager.getModuleModelByDependency(dependency[1], projectModel);
    if (!moduleModelByDependency) {
      return undefined;
    }
    return projectModel.getTarget(moduleModelByDependency.getName(), harTargetData);
  }
}

export interface Dep2RuntimeOnlyObj {
  dep2RuntimeOnlySources: Map<Dependency, string[]>;
  dep2RuntimeOnlyPackages: Map<Dependency, string[]>;
}

export interface Dep2ConfigObj {
  dep2Workers: Map<Dependency, string[]>;
  dep2RuntimeOnlyObj: Dep2RuntimeOnlyObj;
  dep2RouterMapObjPageSourceFiles: Map<Dependency, string[]>;
  dep2Resources: Map<Dependency, string[]>;
}
